#!/usr/bin/env python3
"""
语音提醒模块
支持TTS和预录音频播放
"""

import os
import time
import threading
import logging
import queue
from pathlib import Path
from typing import Dict, Optional, Union
import subprocess
import json

logger = logging.getLogger(__name__)

class VoiceAlert:
    """语音提醒类"""
    
    def __init__(self, 
                 use_tts: bool = True,
                 audio_dir: str = "audio",
                 volume: float = 0.8,
                 language: str = "zh"):
        """
        初始化语音提醒
        
        Args:
            use_tts: 是否使用TTS，False则使用预录音频
            audio_dir: 音频文件目录
            volume: 音量 (0.0-1.0)
            language: 语言代码
        """
        self.use_tts = use_tts
        self.audio_dir = Path(audio_dir)
        self.volume = volume
        self.language = language
        
        # 创建音频目录
        self.audio_dir.mkdir(exist_ok=True)
        
        # 语音消息模板
        self.voice_templates = {
            'zh': {
                'low': {
                    '正常': "前方道路正常",
                },
                'medium': {
                    '台阶': "注意，前方有台阶",
                    '杆子': "注意，前方有杆子",
                },
                'high': {
                    '路口': "警告，前方是路口，请小心",
                    '路障': "警告，前方有路障，请绕行",
                }
            },
            'en': {
                'low': {
                    '正常': "Path ahead is clear",
                },
                'medium': {
                    '台阶': "Caution, steps ahead",
                    '杆子': "Caution, pole ahead",
                },
                'high': {
                    '路口': "Warning, intersection ahead, be careful",
                    '路障': "Warning, obstacle ahead, please detour",
                }
            }
        }
        
        # 音频播放队列
        self.audio_queue = queue.Queue()
        self.is_playing = False
        self.play_thread = None
        
        # 初始化TTS或检查音频文件
        self._initialize()
        
        # 启动播放线程
        self._start_audio_thread()
    
    def _initialize(self):
        """初始化语音系统"""
        if self.use_tts:
            self._check_tts_availability()
        else:
            self._check_audio_files()
    
    def _check_tts_availability(self):
        """检查TTS可用性"""
        try:
            # 检查espeak是否可用
            result = subprocess.run(['espeak', '--version'], 
                                  capture_output=True, text=True)
            if result.returncode == 0:
                self.tts_engine = 'espeak'
                logger.info("使用espeak TTS引擎")
                return
        except FileNotFoundError:
            pass
        
        try:
            # 检查festival是否可用
            result = subprocess.run(['festival', '--version'], 
                                  capture_output=True, text=True)
            if result.returncode == 0:
                self.tts_engine = 'festival'
                logger.info("使用festival TTS引擎")
                return
        except FileNotFoundError:
            pass
        
        try:
            # 检查pyttsx3是否可用
            import pyttsx3
            self.tts_engine = 'pyttsx3'
            self._init_pyttsx3()
            logger.info("使用pyttsx3 TTS引擎")
            return
        except ImportError:
            pass
        
        logger.warning("未找到可用的TTS引擎，切换到预录音频模式")
        self.use_tts = False
        self._check_audio_files()
    
    def _init_pyttsx3(self):
        """初始化pyttsx3"""
        import pyttsx3
        
        self.tts = pyttsx3.init()
        
        # 设置语音参数
        voices = self.tts.getProperty('voices')
        if voices:
            # 尝试选择中文语音
            for voice in voices:
                if 'chinese' in voice.name.lower() or 'zh' in voice.id.lower():
                    self.tts.setProperty('voice', voice.id)
                    break
        
        # 设置语速和音量
        self.tts.setProperty('rate', 150)  # 语速
        self.tts.setProperty('volume', self.volume)  # 音量
    
    def _check_audio_files(self):
        """检查预录音频文件"""
        required_files = []
        
        for risk_level in ['low', 'medium', 'high']:
            for obstacle_type in self.voice_templates['zh'][risk_level].keys():
                filename = f"{obstacle_type}_{risk_level}.wav"
                file_path = self.audio_dir / filename
                
                if not file_path.exists():
                    required_files.append(filename)
        
        if required_files:
            logger.warning(f"缺少音频文件: {required_files}")
            self._generate_audio_files(required_files)
    
    def _generate_audio_files(self, required_files: list):
        """生成缺失的音频文件"""
        logger.info("正在生成音频文件...")
        
        for filename in required_files:
            # 解析文件名
            parts = filename.replace('.wav', '').split('_')
            if len(parts) >= 2:
                obstacle_type = '_'.join(parts[:-1])
                risk_level = parts[-1]
                
                # 获取文本
                text = self.voice_templates['zh'][risk_level].get(obstacle_type, "")
                if text:
                    self._generate_single_audio(text, filename)
    
    def _generate_single_audio(self, text: str, filename: str):
        """生成单个音频文件"""
        file_path = self.audio_dir / filename
        
        try:
            if self.tts_engine == 'espeak':
                # 使用espeak生成
                cmd = [
                    'espeak', 
                    '-v', 'zh',  # 中文
                    '-s', '150',  # 语速
                    '-w', str(file_path),  # 输出文件
                    text
                ]
                subprocess.run(cmd, check=True)
                
            elif self.tts_engine == 'pyttsx3':
                # 使用pyttsx3生成
                self.tts.save_to_file(text, str(file_path))
                self.tts.runAndWait()
            
            logger.info(f"生成音频文件: {filename}")
            
        except Exception as e:
            logger.error(f"生成音频文件失败 {filename}: {e}")
    
    def _start_audio_thread(self):
        """启动音频播放线程"""
        self.is_playing = True
        self.play_thread = threading.Thread(target=self._audio_player)
        self.play_thread.start()
        logger.info("音频播放线程启动")
    
    def _audio_player(self):
        """音频播放工作线程"""
        while self.is_playing:
            try:
                # 获取播放任务
                audio_task = self.audio_queue.get(timeout=1.0)
                
                if audio_task is None:  # 停止信号
                    break
                
                # 播放音频
                self._play_audio(audio_task)
                
            except queue.Empty:
                continue
            except Exception as e:
                logger.error(f"音频播放错误: {e}")
    
    def _play_audio(self, audio_task: dict):
        """播放音频"""
        try:
            if self.use_tts:
                # 使用TTS播放
                self._play_tts(audio_task['text'])
            else:
                # 播放音频文件
                self._play_audio_file(audio_task['file_path'])
                
        except Exception as e:
            logger.error(f"音频播放失败: {e}")
    
    def _play_tts(self, text: str):
        """使用TTS播放文本"""
        try:
            if self.tts_engine == 'espeak':
                cmd = ['espeak', '-v', 'zh', '-s', '150', text]
                subprocess.run(cmd)
                
            elif self.tts_engine == 'festival':
                cmd = ['festival', '--tts']
                process = subprocess.Popen(cmd, stdin=subprocess.PIPE, text=True)
                process.communicate(input=text)
                
            elif self.tts_engine == 'pyttsx3':
                self.tts.say(text)
                self.tts.runAndWait()
                
        except Exception as e:
            logger.error(f"TTS播放失败: {e}")
    
    def _play_audio_file(self, file_path: Path):
        """播放音频文件"""
        if not file_path.exists():
            logger.warning(f"音频文件不存在: {file_path}")
            return
        
        try:
            # 尝试使用aplay (ALSA)
            cmd = ['aplay', str(file_path)]
            result = subprocess.run(cmd, capture_output=True)
            
            if result.returncode != 0:
                # 尝试使用paplay (PulseAudio)
                cmd = ['paplay', str(file_path)]
                subprocess.run(cmd, check=True)
                
        except FileNotFoundError:
            try:
                # 尝试使用pygame
                import pygame
                pygame.mixer.init()
                pygame.mixer.music.load(str(file_path))
                pygame.mixer.music.play()
                
                # 等待播放完成
                while pygame.mixer.music.get_busy():
                    time.sleep(0.1)
                    
            except ImportError:
                logger.error("无法找到音频播放工具")
        except Exception as e:
            logger.error(f"音频文件播放失败: {e}")
    
    def alert(self, obstacle_type: str, risk_level: str, distance_m: Optional[float] = None):
        """发出语音提醒"""
        try:
            # 生成提醒文本（支持距离播报）
            text = None
            if distance_m is not None and obstacle_type != '正常':
                try:
                    meters = max(0.0, min(float(distance_m), 10.0))
                    caution = '请绕行' if risk_level == 'high' else '请小心'
                    text = f"前方{meters:.1f}米有{obstacle_type}，{caution}"
                except Exception:
                    text = None
            if not text:
                text = self.voice_templates[self.language][risk_level].get(obstacle_type)
            
            if not text:
                logger.warning(f"未找到语音模板: {obstacle_type}, {risk_level}")
                return
            
            # 创建播放任务
            if self.use_tts:
                audio_task = {'text': text}
            else:
                filename = f"{obstacle_type}_{risk_level}.wav"
                file_path = self.audio_dir / filename
                audio_task = {'file_path': file_path}
            
            # 添加到播放队列
            if not self.audio_queue.full():
                self.audio_queue.put(audio_task)
                logger.debug(f"语音提醒: {text}")
            else:
                logger.warning("音频队列已满，跳过此次提醒")
                
        except Exception as e:
            logger.error(f"语音提醒失败: {e}")
    
    def set_volume(self, volume: float):
        """设置音量"""
        self.volume = max(0.0, min(1.0, volume))
        
        if self.use_tts and self.tts_engine == 'pyttsx3':
            self.tts.setProperty('volume', self.volume)
    
    def test_voice(self):
        """测试语音功能"""
        logger.info("开始语音测试")
        
        test_messages = [
            ('正常', 'low'),
            ('台阶', 'medium'),
            ('杆子', 'medium'),
            ('路口', 'high'),
            ('路障', 'high')
        ]
        
        for obstacle_type, risk_level in test_messages:
            logger.info(f"测试: {obstacle_type} ({risk_level})")
            self.alert(obstacle_type, risk_level)
            time.sleep(2)  # 等待播放完成
        
        logger.info("语音测试完成")
    
    def stop(self):
        """停止语音系统"""
        logger.info("正在停止语音系统...")
        
        self.is_playing = False
        
        # 发送停止信号
        self.audio_queue.put(None)
        
        # 等待线程结束
        if self.play_thread and self.play_thread.is_alive():
            self.play_thread.join(timeout=2.0)
        
        logger.info("语音系统已停止")

class VoiceConfig:
    """语音配置管理"""
    
    def __init__(self, config_file: str = "voice_config.json"):
        self.config_file = Path(config_file)
        self.config = self._load_config()
    
    def _load_config(self) -> dict:
        """加载配置"""
        default_config = {
            "use_tts": True,
            "volume": 0.8,
            "language": "zh",
            "audio_dir": "audio",
            "voice_templates": {
                "zh": {
                    "low": {
                        "正常": "前方道路正常"
                    },
                    "medium": {
                        "台阶": "注意，前方有台阶",
                        "杆子": "注意，前方有杆子"
                    },
                    "high": {
                        "路口": "警告，前方是路口，请小心",
                        "路障": "警告，前方有路障，请绕行"
                    }
                }
            }
        }
        
        if self.config_file.exists():
            try:
                with open(self.config_file, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                    # 合并默认配置
                    for key, value in default_config.items():
                        if key not in config:
                            config[key] = value
                    return config
            except Exception as e:
                logger.error(f"配置文件加载失败: {e}")
        
        return default_config
    
    def save_config(self):
        """保存配置"""
        try:
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(self.config, f, ensure_ascii=False, indent=2)
            logger.info(f"配置已保存: {self.config_file}")
        except Exception as e:
            logger.error(f"配置保存失败: {e}")
    
    def get(self, key: str, default=None):
        """获取配置项"""
        return self.config.get(key, default)
    
    def set(self, key: str, value):
        """设置配置项"""
        self.config[key] = value

def test_voice_alert():
    """测试语音提醒功能"""
    logging.basicConfig(level=logging.INFO)
    
    try:
        # 创建语音提醒实例
        voice_alert = VoiceAlert()
        
        # 运行测试
        voice_alert.test_voice()
        
        # 停止
        voice_alert.stop()
        
    except Exception as e:
        logger.error(f"语音测试失败: {e}")

if __name__ == "__main__":
    test_voice_alert()